package la.bbs558.aop.control;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import la.bbs558.aop.control.anotation.HandleAfter;
import la.bbs558.aop.control.anotation.HandleBefore;
import la.bbs558.aop.control.anotation.HandleReturn;
import la.bbs558.util.ApplicationUtils;
import la.bbs558.util.ExecutorManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;

@Aspect
@Component
@Order
@Slf4j
public class ControlAop {

    // 需要被SpEl解析的模板前缀和后缀 {{ expression  }}
    private static final ParserContext TEMPLATE_PARSER_CONTEXT = new TemplateParserContext("{{", "}}");
    // SpEL解析器
    private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();

    private static final ExecutorService EXECUTOR_SERVICE = ExecutorManager.myExecutor;


    @Before(value = "@annotation(before)")
    public void before(JoinPoint joinPoint, HandleBefore before) {
        executeJoinPoint(joinPoint, before);
    }

    @After(value = "@annotation(after)")
    public void after(JoinPoint joinPoint, HandleAfter after) {
        executeJoinPoint(joinPoint, after);
    }

    @AfterReturning(value = "@annotation(returning)", returning = "result")
    public void afterReturning(JoinPoint joinPoint,HandleReturn returning, Object result) {
        executeJoinPointReturn(joinPoint,returning, result);
    }

    private static void executeJoinPoint(JoinPoint joinPoint, Annotation annotation) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 参数
        Object[] args = joinPoint.getArgs();
        // 参数名称
        String[] parameterNames = signature.getParameterNames();
        // 目标方法
        // Method targetMethod = signature.getMethod();
        // request
        // HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // response
        // HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        // 方法上的日志注解
        String expression = "";
        Class<? extends Handler> handlerClass = null;
        if (annotation instanceof HandleBefore) {
            handlerClass = ((HandleBefore) annotation).handler();
            expression = ((HandleBefore) annotation).paramExpression();
        } else if (annotation instanceof HandleAfter) {
            handlerClass = ((HandleAfter) annotation).handler();
            expression = ((HandleAfter) annotation).paramExpression();
        }

        EvaluationContext evaluationContext = new StandardEvaluationContext();
        for (int i = 0; i < args.length; i++) {
            evaluationContext.setVariable(parameterNames[i], args[i]);
        }
        Handler handler = ApplicationUtils.getBean(handlerClass);
        handlerExecute(expression, handler::handle, evaluationContext, null);
    }

    private static void executeJoinPointReturn(JoinPoint joinPoint, HandleReturn handleReturn, Object result) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String[] parameterNames = methodSignature.getParameterNames();
        Object[] args = joinPoint.getArgs();
        EvaluationContext paramsEvaluationContext = new StandardEvaluationContext();
        for (int i = 0; i < args.length; i++) {
            paramsEvaluationContext.setVariable(parameterNames[i], args[i]);
        }
        // request
        // HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // response
        // HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        // 方法上的日志注解
        Class<? extends Handler> handlerParamClass = handleReturn.paramHandler();
        Class<? extends Handler> handlerResultClass = handleReturn.resultHandler();
        String paramExpression = handleReturn.paramExpression();
        String resultExpression = handleReturn.resultExpression();
        Handler handlerParam = ApplicationUtils.getBean(handlerParamClass);
        Consumer<Object> paramsConsumer = getHandlerConsumer(handleReturn.paramsAsync(), handlerParam);
        handlerExecute(paramExpression, paramsConsumer,paramsEvaluationContext,null);
        Handler handlerResult = ApplicationUtils.getBean(handlerResultClass);
        Consumer<Object> resultConsumer = getHandlerConsumer(handleReturn.resultAsync(), handlerResult);
        handlerExecute(resultExpression, resultConsumer,null, result);
    }

    @NotNull
    private static Consumer<Object> getHandlerConsumer(boolean handleReturn, Handler handlerResult) {
        return p -> {
            if (handleReturn) {
                EXECUTOR_SERVICE.execute(() -> handlerResult.handle(p));
            } else {
                handlerResult.handle(p);
            }
        };
    }

    private static void handlerExecute(String expression, Consumer<Object> handle, EvaluationContext evaluationContext, Object result) {
        if(evaluationContext == null){
            evaluationContext = new StandardEvaluationContext();
        }
        if (StringUtils.isNotBlank(expression) && result != null) {
            Map<String, Object> resultMap = BeanUtil.beanToMap(result, false, true);
            if (MapUtil.isEmpty(resultMap)) {
                evaluationContext.setVariable(result.getClass().getSimpleName(), result);
            } else {
                resultMap.forEach(evaluationContext::setVariable);
            }
        }
        Object params = null;
        try {
            // SpEL解析的上下文，把 HandlerMethod 的形参都添加到上下文中，并且使用参数名称作为KEY
            if (StringUtils.isNotBlank(expression)) {
                params = EXPRESSION_PARSER.parseExpression(expression, TEMPLATE_PARSER_CONTEXT).getValue(evaluationContext);
                log.info("SpEL表达式解析参数：{}", params);
            } else {
                params = result;
            }
        } catch (Exception e) {
            log.error("SpEL表达式解析异常: {}", e.getMessage());
        }
        try {
            if (handle != null) {
                handle.accept(params);
            }
        } catch (Exception e) {
            log.error("{}.handle({})异常: {}", handle, JSON.toJSONString(params), e.getMessage());
        }
    }
}